package pathfinding

import (
	"bytes"
	"sync"
	"sync/atomic"

	"gddgame.cc/galaxy/utils/serialize"
)

//go:generate msgp

var (
	StrategyMaskPool = sync.Pool{New: func() interface{} {
		return &StrategyMask{}
	}}
	StrategyExtendPool = sync.Pool{New: func() interface{} {
		return &StrategyExtend{}
	}}
	StrategyBlockRequestPool = sync.Pool{New: func() interface{} {
		return &StrategyBlockRequest{}
	}}
	StrategyBlockResponsePool = sync.Pool{New: func() interface{} {
		return &StrategyBlockResponse{}
	}}
)

// 寻路模式
//msgp:tuple StrategyMask
type StrategyMask struct {
	// 友方寻路掩码：确保哪些节点可以移动
	Self int `json:"self" msg:"self"`
	// 非友方寻路掩码：确保哪些节点可以移动
	Other int `json:"other" msg:"other"`
	// 当前对象身份
	Identity int `json:"identity" msg:"identity"`
	// 固定不可走的节点
	Filter [][]byte `json:"filter" msg:"filter"`
	// 固定可走的节点
	Pass [][]byte `json:"pass" msg:"pass"`
	// 移动中对于范围内节点身份过滤（节点上当前包含的）
	IdentityDistance int `json:"identityDistance" msg:"identityDistance"`
	// 寻路时间
	Timestamp int64 `json:"timestamp" msg:"timestamp"`
	// 目标，达到则停止
	NearItem []byte `json:"nearItem" msg:"nearItem"`
	// 起点，过滤寻路
	OriginItem []byte `json:"originItem" msg:"originItem"`
	// 目标范围点，到达则停止移动
	TargetPositions [][]byte `json:"targetPositions" msg:"targetPositions"`

	FilterList    Vector2Slice `json:"-" msg:"-"`
	PassList      Vector2Slice `json:"-" msg:"-"`
	TargetList    Vector2Slice `json:"-" msg:"-"`
	StartPosition *Vector2     `json:"-" msg:"-"`
	Instance      RuleInstance `json:"-" msg:"-"`

	alloc int32
}

func (mask *StrategyMask) Release() {
	if mask.alloc > 0 {
		atomic.AddInt32(&mask.alloc, -1)
		return
	}
	mask.alloc = 0
	StrategyMaskPool.Put(mask)
}
func (mask *StrategyMask) Alloc() {
	atomic.AddInt32(&mask.alloc, 1)
}

func (mask *StrategyMask) Init(center *Vector2, instance RuleInstance) {
	mask.FilterList = DecodeVector2Slice(mask.Filter)
	mask.PassList = DecodeVector2Slice(mask.Pass)
	mask.TargetList = DecodeVector2Slice(mask.TargetPositions)

	center.TailorList(mask.FilterList)
	center.TailorList(mask.PassList)
	center.TailorList(mask.TargetList)
	mask.Instance = instance
}

func (mask *StrategyMask) BindStart(startPosition *Vector2, node PathSeatNode) {
	mask.FilterStart(startPosition)
	mask.StartPosition = startPosition
	if node != nil && node.Exist() {
		mask.OriginItem = node.GetItem()
	}
}

func (mask *StrategyMask) BindEnd(endPosition *Vector2, node PathSeatNode) {
	if node != nil && node.Exist() {
		mask.NearItem = node.GetItem()
	}
}

func (mask *StrategyMask) FilterStart(startPosition *Vector2) {
	for index, p := range mask.FilterList {
		if p.Equals(startPosition) {
			switch index {
			case 0:
				mask.Filter = mask.Filter[1:]
			case len(mask.Filter) - 1:
				mask.Filter = mask.Filter[0:index]
			default:
				mask.Filter = append(mask.Filter[:index], mask.Filter[index+1:]...)
			}
			break
		}
	}
}

func (mask *StrategyMask) SpecialPass(point *Vector2) bool {
	if mask.Pass != nil {
		for _, v := range mask.PassList {
			if v.Equals(point) {
				return true
			}
		}
	}
	return false
}

func (mask *StrategyMask) SpecialFilter(point *Vector2) bool {
	if mask.Filter != nil {
		for _, v := range mask.FilterList {
			if v.Equals(point) {
				return true
			}
		}
	}
	return false
}

func (mask *StrategyMask) IsTargetPositions(point *Vector2) bool {
	if mask.TargetList != nil {
		for _, v := range mask.TargetList {
			if v.Equals(point) {
				return true
			}
		}
	}
	return false
}

func (mask *StrategyMask) NeedChangeTarget() bool {
	return mask.TargetList != nil && len(mask.TargetPositions) > 0
}

func (mask *StrategyMask) IsNearItem(item []byte) bool {
	if mask.NearItem == nil {
		return false
	}
	return bytes.Equal(mask.NearItem, item)
}
func (mask *StrategyMask) IsOriginItem(item []byte) bool {
	if mask.OriginItem == nil {
		return false
	}
	return bytes.Equal(mask.OriginItem, item)
}
func (mask *StrategyMask) CanPathWithSeat(node PathSeatNode) bool {
	if !node.Exist() {
		return true
	}
	seat := node.GetSeat()
	if seat == nil {
		return true
	}
	return (seat.EndTime == 0 || seat.EndTime > mask.Timestamp) && seat.Identity&mask.Identity > 0
}

func (mask *StrategyMask) CanPath(point *Vector2, node PathSeatNode) bool {
	if ok := mask.SpecialFilter(point); ok {
		return false
	}
	if !node.Exist() {
		return true
	}
	t := node.GetType()
	if t == 0 {
		return true
	}
	item := node.GetItem()
	if item != nil {
		//fmt.Println("item", point, mask.OriginItem, mask.NearItem, item)
		if mask.IsOriginItem(item) {
			return true
		}
		if mask.IsNearItem(item) {
			return true
		}
	}
	if ok := mask.SpecialPass(point); ok {
		return true
	}

	identity := node.GetIdentity()
	//fmt.Println("can path", identity, t, mask.Other, mask.Self)
	if identity == 0 || identity&mask.Identity == 0 {
		if (1<<(t-1))&mask.Other == 0 {
			return false
		}
	} else {
		if (1<<(t-1))&mask.Self == 0 {
			//fmt.Println("can self", mask.Self)
			return false
		}
	}
	// 距离内，进行零时身份判断
	if mask.IdentityDistance > 0 && int(mask.Instance.Distance(mask.StartPosition, point)) < mask.IdentityDistance {
		return mask.CanPathWithSeat(node)
	}
	return true
}

func (mask *StrategyMask) GetIdentity() int {
	return mask.Identity
}

//msgp:tuple StrategyExtend
type StrategyExtend struct {
	Time    int64  `json:"duration" msg:"duration"`
	Visible int    `json:"visible" msg:"visible"`
	Next    []byte `json:"next" msg:"next"`

	alloc int32
}

func (extend *StrategyExtend) Get() []byte {
	b, _ := extend.MarshalMsg(nil)
	return b
}
func (extend *StrategyExtend) Release() {
	if extend.alloc > 0 {
		atomic.AddInt32(&extend.alloc, -1)
		return
	}
	extend.alloc = 0
	StrategyMaskPool.Put(extend)
}
func (extend *StrategyExtend) Alloc() {
	atomic.AddInt32(&extend.alloc, 1)
}

// 寻路阻挡逻辑
//msgp:tuple StrategyBlockRequest
type StrategyBlockRequest struct {
	// 移动终点目标
	Goal []byte `json:"goal" msg:"goal"`
	// 当前视野状态
	Visible int `json:"visible" msg:"visible"`
	// 是否是终点
	Arrive bool `json:"arrive" msg:"arrive"`
	// 当前对象序列化
	Item []byte `json:"item" msg:"item"`
	// 移动单个节点时间
	GapTime int `json:"gapTime" msg:"gapTime"`
	//
	PreSeat bool `json:"preSeat" msg:"preSeat"`
	// 移动上一节点
	From []byte        `json:"from" msg:"from"`
	Mask *StrategyMask `json:"mask" msg:"mask"`

	Position []byte `json:"position" msg:"position"`

	alloc int32
}

func (request *StrategyBlockRequest) Release() {
	if request.alloc > 0 {
		atomic.AddInt32(&request.alloc, -1)
		return
	}
	request.alloc = 0
	StrategyBlockRequestPool.Put(request)
}
func (request *StrategyBlockRequest) Alloc() {
	atomic.AddInt32(&request.alloc, 1)
}

//msgp:tuple StrategyBlockResponse
type StrategyBlockResponse struct {
	// 已经到达
	NeedArrive bool `json:"needArrive" msg:"needArrive"`
	// 需要重新寻路：可能地图产生变化或者其他逻辑
	NeedPathFinding bool `json:"needPathFinding" msg:"needPathFinding"`
	// 后续需要过滤某些节点：目标节点的目标的下一个移动节点
	FilterPosition []byte `json:"filterPosition" msg:"filterPosition"`
	// 阻挡时间
	BlockTime int64 `json:"blockTime" msg:"blockTime"`
	// 掩护对象
	CrossItem []byte `json:"crossItem" msg:"crossItem"`
	// 掩护时间
	CrossTime int64 `json:"crossTime" msg:"crossTime"`

	alloc int32
}

func (response *StrategyBlockResponse) Release() {
	if response.alloc > 0 {
		atomic.AddInt32(&response.alloc, -1)
		return
	}
	response.alloc = 0
	StrategyBlockResponsePool.Put(response)
}
func (response *StrategyBlockResponse) Alloc() {
	atomic.AddInt32(&response.alloc, 1)
}

func (request *StrategyBlockRequest) Init(grid *Grid, startPosition *Vector2, instance RuleInstance) {
	request.Mask.Init(grid.start, instance)
	if request.Goal != nil && len(request.Goal) > 0 {
		goal := DecodeVector2(request.Goal)
		grid.start.Tailor(goal)
		_, goalNode := grid.Node(goal)
		request.Mask.BindEnd(goal, goalNode)
	}
	if startPosition != nil {
		_, startNode := grid.Node(startPosition)
		request.Mask.BindStart(startPosition, startNode)
	}
}

func (request *StrategyBlockRequest) GetItem() []byte {
	return request.Item
}

func (request *StrategyBlockRequest) GetMask() PathMask {
	return request.Mask
}

func (request *StrategyBlockRequest) processSeat(timestamp int64, seat *Seat, response *StrategyBlockResponse) {
	stopTime := timestamp - int64(request.GapTime)
	//fmt.Println("process Seat", request.Item, seat, seat.Other, timestamp)
	isPartner := request.Mask.Identity&seat.Identity > 0
	//fmt.Println("process seat", request.Identity, seat.Identity, isPartner)
	block := int64(0)
	sStatus := seat.EndTime == 0 || seat.EndTime > stopTime
	//bVisible := 0
	bNext := []byte{}
	var currentSeat *Seat
	if seat.Other != nil && seat.Other.EndTime > stopTime {
		currentSeat = seat.Other
	} else {
		currentSeat = seat
	}
	if currentSeat.InlineExtend != nil {
		extend := currentSeat.InlineExtend.(*StrategyExtend)
		block = extend.Time
		//bVisible = extend.Visible
		bNext = extend.Next
	} else {
		// 使用默认值，假设有物体停留，可见
	}
	if !sStatus {

	} else if !isPartner {
		//fmt.Println("no partner")
		response.NeedPathFinding = true
		response.FilterPosition = bNext
	} else {
		//fmt.Println("run", timestamp, seat.EndTime, seat.StartTime)
		if sStatus && request.Arrive {
			// 如果终点有人，则换终点
			//fmt.Println("is arrive, but no arrive")
			response.NeedPathFinding = true
		} else if seat.EndTime > 0 && timestamp < seat.EndTime {
			//fmt.Println("end", seat.EndTime, timestamp)
			// 等待部队移动走
			response.BlockTime = seat.EndTime + 100
		} else if timestamp < seat.StartTime+int64(request.GapTime*2) {
			//fmt.Println("start", seat.StartTime, timestamp)
			// 等部队移动到达
			response.BlockTime = seat.StartTime + int64(request.GapTime*2) + 100
		} else if sStatus && (seat.Other == nil || (seat.Other.EndTime > 0 && seat.Other.EndTime < stopTime)) {
			//fmt.Println("cross", request.GapTime, timestamp)
			// 上面有部队停留，穿越
			response.CrossItem = seat.Item
			response.CrossTime = timestamp + int64(request.GapTime*2) + 100
		} else if block > timestamp {
			//fmt.Println("strategy", bNext, request.From)
			if bNext != nil && bytes.Equal(bNext, request.From) {
				// 对方向自己移动，则自己重新寻路
				// 互相穿越
				//fmt.Println("from equal next")
				response.NeedPathFinding = true
			} else {
				// 等待时间后，通过
				//fmt.Println("block", block, timestamp)
				response.BlockTime = block + int64(request.GapTime) + 100
			}
		} else {
			//fmt.Println("need pathfinding")
			response.NeedPathFinding = true
		}
	}
}

func (request *StrategyBlockRequest) ProcessNode(position *Vector2, timestamp int64, node PathSeatNode, step int) (serialize.AutoIStruct, *BaseSeat) {
	response := &StrategyBlockResponse{
		NeedArrive:      false,
		NeedPathFinding: false,
		FilterPosition:  nil,
		BlockTime:       0,
		CrossItem:       []byte{},
		CrossTime:       0,
	}
	var pre *BaseSeat
	item := node.GetItem()
	if item != nil {
		// 寻路能走到的建筑物只有终点
		// 对于目的地是建筑，则停止移动
		response.NeedArrive = true
		//fmt.Println("node", request.Position, request.Arrive, request.Goal)
		// 在寻路
		//} else if request.Mask.IsTargetPositions(position) {
		//	response.NeedArrive = true
	}

	seat := node.GetSeat()
	if seat != nil {
		request.processSeat(timestamp, seat, response)
	}
	if request.PreSeat && !response.NeedArrive && !response.NeedPathFinding && response.BlockTime == 0 {
		// 离目标几格"给予2倍的锁定时间，超过时间后会被移除
		if step-1 > 0 {
			timestamp += int64(request.GapTime * 2 * (step - 1) * 2)
		}
		// 移动到下一格的时间
		stayTime := timestamp + int64(request.GapTime*2)
		if !request.Arrive {
			// 如果不停留，则加上离开时间
			stayTime += int64(request.GapTime)
		}
		// 如果不绕路，则预处理添加
		//extend := &StrategyExtend{
		//	Time:    stayTime + 100,
		//	Visible: request.Visible,
		//	Next:    nil,
		//}
		// 阻挡时间后移动半格到达
		nextTime := timestamp + int64(request.GapTime)
		pre = &BaseSeat{
			Item:      request.Item,
			StartTime: nextTime,
			//EndTime:   0,
			//Identity:  request.Identity,
			//Extend:    extend.Get(),
		}
	}

	return response, pre
}
